import dayjs from 'dayjs';
import { describe, expect, it } from 'vitest';
import {
  generateId,
  Identify,
  sexValue,
  validateId,
} from '../../src/hammer/identify.mts';

describe('身份证相关函数', () => {
  describe('generateId 身份证生成函数', () => {
    it('应该生成有效的男性身份证', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
      });
      expect(typeof id).toBe('string');
      expect(id).toMatch(/^\d{17}[\dX]$/);
      expect(validateId(id as string)).toBe(true);
    });

    it('应该生成有效的女性身份证', () => {
      const id = generateId({
        sex: sexValue.female,
        birthYear: 1985,
        birthMonth: 12,
        birthDay: 25,
        countyCode: 310101,
      });
      expect(typeof id).toBe('string');
      expect(validateId(id as string)).toBe(true);
    });

    it('当count>1时应该批量生成多个身份证', () => {
      const ids = generateId({
        sex: sexValue.male,
        birthYear: 2000,
        birthMonth: 1,
        birthDay: 1,
        countyCode: 440305,
        count: 3,
      }) as string[];
      expect(Array.isArray(ids)).toBe(true);
      expect(ids).toHaveLength(3);
      ids.forEach((id) => {
        expect(typeof id).toBe('string');
        expect(validateId(id)).toBe(true);
      });
    });

    it('应该对县级代码进行前导零填充', () => {
      const id = generateId({
        sex: sexValue.female,
        birthYear: 1995,
        birthMonth: 7,
        birthDay: 20,
        countyCode: 1,
      }) as string;
      expect(id.startsWith('000001')).toBe(true);
    });

    it('批量生成时应该确保身份证号码唯一', () => {
      const ids = generateId({
        sex: sexValue.male,
        birthYear: 2000,
        birthMonth: 1,
        birthDay: 1,
        countyCode: 110105,
        count: 500, // 生成大量号码测试去重
      }) as string[];

      expect(Array.isArray(ids)).toBe(true);
      expect(ids.length).toBeLessThanOrEqual(500);

      // 验证所有号码都是唯一的
      const uniqueIds = new Set(ids);
      expect(uniqueIds.size).toBe(ids.length);

      // 验证所有号码都有效
      ids.forEach((id) => {
        expect(validateId(id)).toBe(true);
      });
    });

    it('单次批量生成的号码应该互不相同', () => {
      const ids = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 10,
      });

      expect(Array.isArray(ids)).toBe(true);
      // 单次批量生成的号码应该互不相同
      const uniqueIds = new Set(ids as string[]);
      expect(uniqueIds.size).toBe((ids as string[]).length);
      expect(ids).toHaveLength(10);
    });

    it('应该支持乱序生成身份证', () => {
      const ids1 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 5,
        shuffle: true,
      });

      const ids2 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 5,
        shuffle: true,
      });

      expect(Array.isArray(ids1)).toBe(true);
      expect(Array.isArray(ids2)).toBe(true);
      expect(ids1).toHaveLength(5);
      expect(ids2).toHaveLength(5);

      // 由于乱序，两次生成的结果应该不同（概率很高）
      expect(ids1).not.toEqual(ids2);

      // 验证所有号码都有效
      [...(ids1 as string[]), ...(ids2 as string[])].forEach((id) => {
        expect(validateId(id)).toBe(true);
      });
    });

    it('应该支持顺序生成身份证（shuffle=false）', () => {
      const ids1 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 3,
        shuffle: false,
      });

      const ids2 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 3,
        shuffle: false,
      });

      expect(ids1).toEqual(ids2);
      expect(ids1).toHaveLength(3);

      // 验证所有号码都有效
      (ids1 as string[]).forEach((id) => {
        expect(validateId(id)).toBe(true);
      });
    });

    it('应该支持乱序生成单个身份证', () => {
      const id1 = generateId({
        sex: sexValue.female,
        birthYear: 1995,
        birthMonth: 8,
        birthDay: 20,
        countyCode: 310101,
        shuffle: true,
      }) as string;

      const id2 = generateId({
        sex: sexValue.female,
        birthYear: 1995,
        birthMonth: 8,
        birthDay: 20,
        countyCode: 310101,
        shuffle: true,
      }) as string;

      expect(typeof id1).toBe('string');
      expect(typeof id2).toBe('string');
      expect(validateId(id1)).toBe(true);
      expect(validateId(id2)).toBe(true);
    });

    it('应该正确处理count参数的边界值', () => {
      // count为0时应该返回空数组
      const ids0 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 0,
      });
      expect(Array.isArray(ids0)).toBe(true);
      expect(ids0 as string[]).toHaveLength(0);

      // count为1时应该返回字符串
      const id1 = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 5,
        birthDay: 15,
        countyCode: 110105,
        count: 1,
      });
      expect(typeof id1).toBe('string');
      expect(validateId(id1 as string)).toBe(true);
    });
  });

  describe('validateId 身份证验证函数', () => {
    it('应该验证通过generateId生成的身份证', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      expect(validateId(id)).toBe(true);
    });

    it('应该拒绝无效长度的身份证', () => {
      expect(validateId('12345678901234567')).toBe(false);
      expect(validateId('1234567890123456789')).toBe(false);
    });

    it('应该拒绝无效格式的身份证', () => {
      expect(validateId('12345678901234567A')).toBe(false);
      expect(validateId('abcdefghijklmnopqr')).toBe(false);
    });

    it('应该拒绝无效省份代码的身份证', () => {
      expect(validateId('99010519900307603X')).toBe(false);
    });

    it('应该拒绝无效出生日期的身份证', () => {
      expect(validateId('11010519901337603X')).toBe(false);
      expect(validateId('11010519900332003X')).toBe(false);
    });

    it('应该拒绝无效校验码的身份证', () => {
      const validId = generateId({
        sex: sexValue.female,
        birthYear: 1985,
        birthMonth: 12,
        birthDay: 25,
        countyCode: 310101,
      }) as string;
      const invalidId = `${validId.slice(0, -1)}0`;
      expect(validateId(invalidId)).toBe(false);
    });

    it('应该正确处理边界出生日期', () => {
      const validId1 = generateId({
        sex: sexValue.male,
        birthYear: 1900,
        birthMonth: 1,
        birthDay: 1,
        countyCode: 110105,
      }) as string;
      expect(validateId(validId1)).toBe(true);

      const validId2 = generateId({
        sex: sexValue.female,
        birthYear: 2023,
        birthMonth: 12,
        birthDay: 31,
        countyCode: 310101,
      }) as string;
      expect(validateId(validId2)).toBe(true);
    });
  });

  describe('Identify 身份信息类', () => {
    it('应该正确解析男性身份证信息', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const identify = new Identify(id);

      expect(identify.value).toBe(id);
      expect(identify.valueOf()).toBe(id);
      expect(identify.birthday).toBe('1990-03-07');
      expect(identify.age).toBeGreaterThan(30);
      expect(identify.sex).toBe(sexValue.male);
      expect(identify.sexDesc).toBe('男');
      expect(identify.countyCode).toBe('110105');
    });

    it('应该正确解析女性身份证信息', () => {
      const id = generateId({
        sex: sexValue.female,
        birthYear: 1985,
        birthMonth: 12,
        birthDay: 25,
        countyCode: 310101,
      }) as string;
      const identify = new Identify(id);

      expect(identify.value).toBe(id);
      expect(identify.valueOf()).toBe(id);
      expect(identify.birthday).toBe('1985-12-25');
      expect(identify.age).toBeGreaterThan(30);
      expect(identify.sex).toBe(sexValue.female);
      expect(identify.sexDesc).toBe('女');
      expect(identify.countyCode).toBe('310101');
    });

    it('应该正确解析边界生日日期', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1900,
        birthMonth: 1,
        birthDay: 1,
        countyCode: 110105,
      }) as string;
      const identify = new Identify(id);

      expect(identify.birthday).toBe('1900-01-01');
      expect(identify.age).toBeGreaterThan(100);
    });

    it('应该正确解析性别为其他的情况', () => {
      // 使用generateId生成一个有效的男性身份证
      const maleId = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const maleIdentify = new Identify(maleId);

      // 使用generateId生成一个有效的女性身份证
      const femaleId = generateId({
        sex: sexValue.female,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const femaleIdentify = new Identify(femaleId);

      // 验证男性身份证
      expect(maleIdentify.sex).toBe(sexValue.male);
      expect(maleIdentify.sexDesc).toBe('男');

      // 验证女性身份证
      expect(femaleIdentify.sex).toBe(sexValue.female);
      expect(femaleIdentify.sexDesc).toBe('女');

      // 由于validateId会拒绝无效的身份证号码，我们无法直接测试性别为其他的情况
      // 但我们可以验证sex属性的解析逻辑
    });

    it('应该正确解析区县代码', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const identify = new Identify(id);

      expect(identify.countyCode).toBe('110105');
    });

    it('应该正确返回JSON对象', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const identify = new Identify(id);
      const json = identify.toJson();

      expect(typeof json).toBe('object');
      expect(json.value).toBe(id);
      expect(json.birthday).toBe('1990-03-07');
      expect(typeof json.age).toBe('number');
      expect(json.sex).toBe(sexValue.male);
      expect(json.sexDesc).toBe('男');
      expect(json.countyCode).toBe('110105');
    });

    it('应该正确计算真实年龄', () => {
      const id = generateId({
        sex: sexValue.male,
        birthYear: 1990,
        birthMonth: 3,
        birthDay: 7,
        countyCode: 110105,
      }) as string;
      const identify = new Identify(id);

      // 使用固定日期计算期望的真实年龄，避免动态时间导致测试不稳定
      const currentDate = dayjs('2025-06-01');
      const birthDate = dayjs('1990-03-07');
      const expectedAge = currentDate.diff(birthDate, 'year', true);

      // 由于realAge使用dayjs计算，可能存在小数部分，我们检查它接近期望值
      expect(identify.realAge).toBeCloseTo(expectedAge, 0);
    });

    it('应该拒绝无效的身份证号码', () => {
      const invalidId = '123456789012345678';
      expect(() => new Identify(invalidId)).toThrow('身份证号格式错误');
    });

    it('应该拒绝短身份证号码', () => {
      const shortId = '110105';
      expect(() => new Identify(shortId)).toThrow('身份证号格式错误');
    });

    it('应该拒绝空字符串身份证', () => {
      expect(() => new Identify('')).toThrow('身份证号格式错误');
    });

    it('应该拒绝格式错误但长度正确的身份证', () => {
      const invalidId = '11010519901337603X'; // 13月是无效的
      expect(() => new Identify(invalidId)).toThrow('身份证号格式错误');
    });

    it('应该拒绝无效校验码的身份证', () => {
      const validId = generateId({
        sex: sexValue.female,
        birthYear: 1985,
        birthMonth: 12,
        birthDay: 25,
        countyCode: 310101,
      }) as string;
      const invalidId = `${validId.slice(0, -1)}0`;
      expect(() => new Identify(invalidId)).toThrow('身份证号格式错误');
    });
  });
});
